﻿#pragma once

#include <string>
#include <type_traits>
#ifdef __linux__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
typedef int FileHandle;
typedef mode_t FileMode;
#define RDONLY	O_RDONLY
#define WRONLY	O_WRONLY
#define RDWR	O_RDWR
#define INVALID_FILE_HANDLE	(-1)
#define SEEK_BEGIN	SEEK_SET

#else	//__WIN32__
#include <windows.h>
typedef HANDLE FileHandle;
typedef DWORD FileMode;
#define RDONLY	GENERIC_READ
#define WRONLY	GENERIC_WRITE
#define RDWR	(GENERIC_READ | GENERIC_WRITE)
#define INVALID_FILE_HANDLE INVALID_HANDLE_VALUE
#define SEEK_BEGIN	FILE_BEGIN
#ifndef SEEK_END
#define SEEK_END	FILE_END
#define SEEK_CUR	FILE_CURRENT
#define SEEK_SET	FILE_BEGIN
#endif

#endif

namespace common
{
#if defined(__WIN32) || defined(WIN32) || defined(__WIN32__)
	static inline std::wstring StringToWString(const std::string & str, size_t m_encode = CP_ACP)
	{
		auto size = MultiByteToWideChar(m_encode, 0, str.c_str(), str.size(), NULL, 0);
		std::wstring wstr(size + 1, L'\0');
		wchar_t *wp = const_cast<wchar_t *>(wstr.c_str());
		MultiByteToWideChar(m_encode, 0, str.c_str(), str.size(), wp, size);
		return wstr;
	} 
	
	static inline std::string WStringToString(const std::wstring & wstr, size_t m_encode = CP_ACP)
	{
		auto size = WideCharToMultiByte(m_encode, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
		std::string str(size + 1, '\0');
		char *cp = const_cast<char *>(str.c_str());
		WideCharToMultiByte(m_encode, 0, wstr.c_str(), wstr.size(), cp, size, NULL, NULL);
		return str;
	}
#endif

	class file
	{
	private:
		FileHandle handle;
		void *mapaddr = nullptr;
		std::size_t mapsize = 0;
#if defined(__WIN32__) || defined(WIN32) || defined(_MSC_VER)
		FileHandle mapHandle = nullptr;
#endif
	public:
		file()
		{ 
 			handle = INVALID_FILE_HANDLE; 
		}
		file(const char *path, FileMode mode = RDWR) : file()
		{
            Open(path, mode);
		}
		~file()
		{
			Close();
		}
		
		explicit operator bool() const { return handle != INVALID_FILE_HANDLE; }
        
        bool Open(const char *path, FileMode mode = RDWR)
		{
			if (handle != INVALID_FILE_HANDLE)
                Close();
#ifdef __linux__
			handle = open(path, mode | O_CREAT, 0666);
#else	//__WIN32__
            handle = CreateFileW(StringToWString(path, CP_UTF8).c_str(), mode, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            //handle = CreateFileA(path, mode, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
			return (handle != INVALID_FILE_HANDLE);
		}
        bool Isopen()
        {
            return (INVALID_FILE_HANDLE != handle);
        }

        long Seek(long offset, off_t pos)
		{
			if (handle == INVALID_FILE_HANDLE)
				return -1;
#ifdef __linux__
			return lseek(handle, offset, pos);
#else	//__WIN32__	
			LARGE_INTEGER liDistanceToMove, newFilePosition;
			liDistanceToMove.QuadPart = offset;
			newFilePosition.QuadPart = 0;
			return SetFilePointerEx(handle, liDistanceToMove, &newFilePosition, pos) 
					? (long)newFilePosition.QuadPart : -1;
#endif
		}

		bool Truncate(size_t length)
		{
			if (handle == INVALID_FILE_HANDLE)
				return false;
#ifdef __linux__
			return 0 == ftruncate(handle, length);
#else	//__WIN32__
			long offset = Seek(0, SEEK_CUR);
			if (-1 == Seek(length, SEEK_BEGIN))
				return false;
			bool ret = (0 != SetEndOfFile(handle));
			Seek(offset, SEEK_BEGIN);
			return ret;
#endif
		}

        size_t Read(void *buf, const size_t size)
		{
			if (handle == INVALID_FILE_HANDLE)
				return -1;
            if (size == 0)
                return 0;
#ifdef __linux__
			return read(handle, buf, size);
#else	//__WIN32__
            unsigned long readsize = -1;
			ReadFile(handle, buf, size, &readsize, NULL);
            return readsize;
#endif
		}
        int Read(std::string & str)
        {
			auto size = Size();
			if (size == 0)
				return 0;
			str.resize(size);
            return Read(const_cast<char *>(str.c_str()), str.length());
        }
        size_t Write(const void *buf, const size_t size)
		{
			if (handle == INVALID_FILE_HANDLE)
				return -1;
#ifdef __linux__
			return write(handle, buf, size);
#else	//__WIN32__
            unsigned long writesize = -1;
			WriteFile(handle, buf, size, &writesize, NULL);
            return writesize;
#endif
		}
        size_t Write(const std::string &content)
		{
			if (handle == INVALID_FILE_HANDLE)
				return -1;
#ifdef __linux__
			return write(handle, content.data(), content.size());
#else	//__WIN32__
            unsigned long writesize = -1;
			WriteFile(handle, content.data(), content.size(), &writesize, NULL);
            return writesize;
#endif
		}
		bool Fsync()
		{
			if (handle == INVALID_FILE_HANDLE)
				return false;
#ifdef __linux__
			return (0 == fdatasync(handle));
#else	//__WIN32__
			return FlushFileBuffers(handle) == TRUE;
#endif
		}
        size_t Size()
		{
			long offset, size;
			if (handle == INVALID_FILE_HANDLE)
				return 0;
			offset = Seek(0, SEEK_CUR);
			size = Seek(0, SEEK_END);
			Seek(offset, SEEK_BEGIN);
            return (size == -1) ? 0 : (size_t)size;
		}
		void *Mmap(size_t _mapsize = 0, FileMode mode = RDWR)
		{
			if (mapaddr != nullptr)
				return mapaddr;
			mapsize = (_mapsize > 0) ? _mapsize : Size();
#ifdef __linux__
			void *m = mmap(NULL, mapsize, 
					mode == RDONLY ? PROT_READ : (PROT_READ | PROT_WRITE), 
					MAP_SHARED, handle, 0);
			if (m == MAP_FAILED || m == NULL)
				return nullptr;
			else {
				mapaddr = m;
				return m;
			}
#else	//__WIN32__
			if (mapHandle == NULL) {
				mapHandle = CreateFileMapping(handle, NULL, 
					mode == RDONLY ? PAGE_READONLY : PAGE_READWRITE, 
					0, mapsize, NULL);
				if (mapHandle == NULL) 
					return NULL;
			}
			mapaddr = MapViewOfFile(mapHandle, 
						mode == RDONLY ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 
						0, 0, 0);
			return mapaddr;
#endif
		}
        bool Msync(void *addr, size_t size)
		{
			if (addr == NULL || !(mapaddr != nullptr && addr >= mapaddr && (char *)addr + size <= (char *)mapaddr + size))
				return false;
#ifdef __linux__
			return (msync(addr, size, MS_SYNC) == 0);
#else	//__WIN32__
			return (mapHandle == NULL) ? false : (FlushViewOfFile(addr, size) == TRUE);
#endif
		}
        bool Unmap(void *addr)
		{
			if (addr == NULL || addr != mapaddr)
				return false;
#ifdef __linux__
			bool ret = (0 == munmap(addr, mapsize));
#else	//__WIN32__
			bool ret = (mapHandle == NULL) ? false : (UnmapViewOfFile(addr) == TRUE);
			if (ret) {
				CloseHandle(mapHandle);
				mapHandle = nullptr;
			}
#endif
			if (ret) {
				mapaddr = nullptr;
				mapsize = 0;
			}
			return ret;
		}
		void Close()
		{
			if (handle == INVALID_FILE_HANDLE)
				return;
			if (mapaddr != nullptr)
				Unmap(mapaddr);
#ifdef __linux__
			close(handle);
#else	//__WIN32__
			CloseHandle(handle);
#endif
			handle = INVALID_FILE_HANDLE;
		}
		template<typename T>
		file & operator <<(T && t)
		{
			static_assert(std::is_pod<typename std::decay<T>::type>::value, "The type T isn't pod!");
			Write(&t, sizeof(t));
			return *this;
		}
		template<typename T>
		file & operator >>(T && t)
		{
			static_assert(std::is_pod<typename std::decay<T>::type>::value, "The type T isn't pod!");
			Read(&t, sizeof(t));
			return *this;
		}
        file & operator <<(const std::string & str)
		{
			Write(str.c_str(), str.length());
			return *this;
		}
        file & operator >>(std::string & str)
		{
            Read(str);
			return *this;
		}
	protected:
	};
}
